import Vue,{CreateElement,PropOptions,PropType,Component,VNodeChildren} from 'vue';
import {FieldGetter,LoadStatus,PlainObject,TickStrategy} from '../../../../../types';
import {event} from 'quasar';
import {MySet} from '../../../../util/polyfill';
const {stopAndPrevent}=event;
//region 定义接口
/**
 * 原始对象
 * @property {boolean} [_selectable]		可选中
 * @property {boolean} [_treeTickable]		可勾选
 * @property {boolean} [_treeExpandable]	可展开
 * @property {boolean} [_treeNoTick]		没有勾选框
 * @property {TickStrategy} [_tickStrategy]	勾选策略
 * @property {boolean} [_treeLazy]			懒加载
 */
export interface RawObj extends PlainObject{
	readonly _selectable?:boolean,
	readonly _treeTickable?:boolean,
	readonly _treeExpandable?:boolean,
	readonly _treeNoTick?:boolean,
	readonly _tickStrategy?:TickStrategy,
	readonly _treeLazy?:boolean,
}
/**
 * 计算后的对象信息
 * @property {boolean} visible						是否可见
 * @property {number} index							行索引
 * @property {RawObj} rawObj						原始对象
 * @property {number} tab							缩进
 * @property {ComputedObj|undefined} parent			父行信息
 * @property {boolean} isParent						是否有子行
 * @property {Array<ComputedObj>} children			子行
 * @property {string} key							行唯一键
 * @property {undefined|boolean|LoadStatus} lazy	懒加载状态
 * @property {boolean} matchesFilter				是否匹配过滤
 * @property {TickStrategy} tickStrategy			勾选策略
 * @property {boolean} selected						是否选中
 * @property {boolean} selectable					是否能选中
 * @property {boolean} [indeterminate]				半勾选状态
 * @property {boolean} [indeterminateNextState]		下次勾选状态
 * @property {boolean} ticked						是否勾选
 * @property {boolean} tickable						是否能勾选
 * @property {boolean} expanded						是否展开
 * @property {boolean} expandable					是否能展开
 * @property {boolean} noTick						没有勾选框
 * @property {boolean} lazyNotLoaded				有懒加载, 还未完成
 * @property {boolean} hasTicking					有勾选策略
 * @property {boolean} strictTicking				单独勾选策略
 * @property {boolean} leafTicking					仅勾选末级策略
 * @property {boolean} leafFilteredTicking			仅勾选过滤后的末级策略
 */
export interface ComputedObj{
	visible:boolean,
	index:number,
	rawObj:RawObj,
	tab:number,
	parent:ComputedObj|undefined,
	isParent:boolean,
	children:Array<ComputedObj>,
	key:string,
	lazy:undefined|boolean|LoadStatus,
	matchesFilter:boolean,
	tickStrategy:TickStrategy,
	selected:boolean,
	selectable:boolean,
	indeterminate?:boolean,
	indeterminateNextState?:boolean,
	ticked:boolean,
	tickable:boolean,
	expanded:boolean,
	expandable:boolean,
	noTick:boolean,
	lazyNotLoaded:boolean,
	hasTicking:boolean,
	strictTicking:boolean,
	leafTicking:boolean,
	leafFilteredTicking:boolean,
}
/**
 * 计算后的行信息的映射
 */
export interface MetaMapping{
	[key:string]:ComputedObj,
}
/**
 * 储存懒加载行的懒加载状态
 */
export interface LazyTemp{
	[key:string]:LoadStatus
}
/**
 * 过滤值类型
 */
export type FilterType=string|RegExp;
/**
 * 过滤方法
 * @param param			过滤方法的参数
 * @return {boolean}	是否满足过滤
 */
export interface FilterMethod<P>{
	(param:P):boolean
}
/**
 * 对象形式的过滤值(启用特定的过滤方法)
 * @property {string} filter				过滤值
 * @property {FilterMethod} filterMethod	过滤方法
 */
export interface FilterObj<P>{
	filter:FilterType,
	filterMethod:FilterMethod<P>,
}
/**
 * 操作描述
 * @property {Array<RawObj>} data 操作的数据
 * @property {boolean} state 操作状态
 */
export interface Operation{
	data:Array<RawObj>,
	state:boolean,
}
/**
 * 节点信息
 * @property {ComputedObj} meta		计算后的节点信息
 */
export interface NodeScope{
	meta:ComputedObj,
}
/**
 * 前置处理器
 * @param {NodeScope} scope	节点信息
 * @return {boolean|Promise<boolean>}	是否禁止选中
 */
export interface BeforeProcessor{
	(scope:NodeScope):boolean|Promise<boolean>
}
//endregion
/**
 * 树混合
 * @author 唐国雄
 * @date 2021/7/14
 * 属性----------------------
 * :data				数据列表, Array<RawObj>
 * :obj-key				主键, string | FieldGetter<RawObj>
 * :children-key		子节点属性, string
 * :filter				过滤值, string | FilterObj<void>
 * :filter-method		过滤方法, FilterMethod<void>
 * :tick-strategy		勾选策略, TickStrategy
 * :accordion			同一级只展开一个, boolean
 * :selectable			是否能选中, boolean
 * :selected			选中的行(支持sync), RawObj
 * :tree-ticked			勾选的行(支持sync), MySet<RawObj>
 * :tree-expanded		展开的行(支持sync), MySet<RawObj>
 * :emit-all-ticked		发射所有勾选或者半勾选的节点, boolean
 * :no-leaf-ticking		没有末级勾选策略(用于优化大数据), boolean
 * :before-ticked		勾选前置处理器, BeforeProcessor
 * 事件----------------------
 * @update:tree-ticked		勾选的行, MySet<RawObj>
 * @update:tree-expanded	展开的行, MySet<RawObj>
 * @tree-lazy-load			开始懒加载, {meta:ComputedObj, done:(Array<RawObj>) => void, fail:() => void}
 * @all-ticked				所有勾选或者半勾选的节点, Array<RawObj>
 * @tree-ticked				点击可勾选的勾选框, {meta:ComputedObj, state:boolean}
 * 方法----------------------
 * setExpandedUnlimited(opts:Array<Operation>)		设置行的展开状态(无视限制)
 * setTickedUnlimited(opts:Array<Operation>)		设置行的勾选状态(无视限制)
 * setExpanded(key:string,state:boolean)			设置行的展开状态(带校验), Promise<void>
 * setExpandeds(keys:Array<string>,state:boolean)	批量设置行的展开状态(带校验), Promise<void>
 * setMatchesExpanded(state:boolean)				设置所有匹配节点的展开状态(带校验), Promise<void>
 * expandedToNode(key:string)						展开到当前节点(带校验), Promise<void>
 * setTicked(key:string,state:boolean)				设置行的勾选状态(带校验)
 * setTickeds(keys:Array<string>,state:boolean)		批量设置行的勾选状态(带校验)
 * getMeta(key:string):ComputedObj|undefined		获取行的所有状态信息
 * getMetas():Array<ComputedObj>|undefined			获取所有行的所有状态信息
 */
export default Vue.extend({
	props:{
		//数据列表
		data:{
			type:Array,
			required:true,
		} as PropOptions<Array<RawObj>>,
		//唯一键
		objKey:{
			type:[String,Function],
			required:true,
		} as PropOptions<string|FieldGetter<RawObj>>,
		//子节点字段
		childrenKey:String,
		//过滤值
		filter:{
			type:[String,Object,RegExp],
			default:'',
		} as PropOptions<FilterType|FilterObj<any>>,
		//过滤方法
		filterMethod:Function as PropType<FilterMethod<any>>,
		//勾选策略(必须先设置children-key)
		tickStrategy:{
			type:String,
			default:'none',
			validator(v){
				return [
					'none',
					'strict',
					'leaf',
					'leaf-filtered',
				].indexOf(v)!==-1;
			},
		} as PropOptions<TickStrategy>,
		//同一级只展开一个
		accordion:Boolean,
		//是否能选中
		selectable:{
			type:Boolean,
			default:true,
		},
		//选中的行(支持sync)
		selected:{} as PropOptions<RawObj>,
		//勾选的行(支持sync)
		treeTicked:MySet as PropType<MySet<RawObj>>,
		//展开的行(支持sync)
		treeExpanded:MySet as PropType<MySet<RawObj>>,
		//发射所有勾选或者半勾选的节点
		emitAllTicked:Boolean,
		//没有末级勾选策略(用于优化大数据)
		noLeafTicking:Boolean,
		//勾选前置处理器
		beforeTicked:Function as PropType<BeforeProcessor>,
	},
	data(){
		return {
			//懒加载缓存
			treeLazy:{} as LazyTemp,
			//选中的行
			innerSelected:(this.selected||null) as RawObj|null,
			//勾选的行
			innerTreeTicked:(this.treeTicked||new MySet<RawObj>()) as MySet<RawObj>,
			//展开的行
			innerTreeExpanded:(this.treeExpanded||new MySet<RawObj>()) as MySet<RawObj>,
		};
	},
	computed:{
		//region 当前过滤值
		curFilter():FilterType{
			const filter=this.filter;
			if(typeof filter==='string'||filter instanceof RegExp){
				return filter;
			}
			return filter.filter;
		},
		//endregion
		//region 当前过滤方法
		curFilterMethod():FilterMethod<any>{
			const filter=this.filter;
			if(typeof filter==='string'||filter instanceof RegExp){
				return this.filterMethod;
			}
			return filter.filterMethod;
		},
		//endregion
		//region 树模式, 计算相关数据(偏平化行)
		treeInfo():{computedObjs:Array<ComputedObj>,allComputedObjs:Array<ComputedObj>,metas:MetaMapping}|undefined{
			//在箭头函数中使用this会影响性能(大数据递归时很明显)
			const {
				selectable:globalSelectable,
				innerTreeTicked,
				innerSelected,
				innerTreeExpanded,
				objKey,
				childrenKey,
				tickStrategy:globalTickStrategy,
				curFilter,
				curFilterMethod,
				noLeafTicking,
				treeLazy:globalLazy,
				$_lexmis_getFilterMethodParam:getFilterMethodParam,
			}=this;
			if(!childrenKey) return;
			const allComputedObjs:Array<ComputedObj>=[];
			const metas:MetaMapping={};
			const travel=function(rawObj:RawObj,parent?:ComputedObj,tab:number=0){
				const {
					_selectable,
					_treeTickable,
					_treeExpandable,
					_treeNoTick,
					_tickStrategy,
					_treeLazy,
				}=rawObj;
				let key;
				if(typeof objKey==='string'){
					key=rawObj[objKey];
				}else{
					key=objKey(rawObj);
				}
				const children:Array<RawObj>=rawObj[childrenKey];
				//--------计算需要的信息--------
				const visible:boolean=parent==null||(parent.visible&&parent.expanded);
				const isParent:boolean=(children&&children.length>0)===true;
				const tickStrategy:TickStrategy=_tickStrategy||(parent?parent.tickStrategy:globalTickStrategy);
				const selectable:boolean=_selectable!==false&&globalSelectable;
				const hasTicking:boolean=tickStrategy!=='none';
				const strictTicking:boolean=tickStrategy==='strict';
				const leafFilteredTicking:boolean=tickStrategy==='leaf-filtered';
				const leafTicking:boolean=tickStrategy==='leaf'||leafFilteredTicking;
				//↑↑↑↑↑↑↑↑↑↑↑↑↑↑以上信息不会被改变↑↑↑↑↑↑↑↑↑↑↑↑↑↑
				let lazy:undefined|boolean|LoadStatus=_treeLazy;
				//是懒加载, 就拉取存储的懒加载状态
				if(
					lazy&&
					globalLazy[key]&&
					Array.isArray(children)===true
				){
					lazy=globalLazy[key];
				}
				const lazyNotLoaded:boolean=(lazy&&lazy!=='loaded')===true;
				let noTick:boolean=!hasTicking||_treeNoTick===true;
				//只勾选末级策略下, 父级节点没有勾选框, 则没有勾选框,
				if(leafTicking&&parent&&parent.noTick){
					noTick=true;
				}
				//非单独勾选策略, 懒加载还未完成, 不能勾选
				let tickable:boolean=!noTick&&_treeTickable!==false&&(strictTicking||!lazyNotLoaded);
				//只勾选末级策略下, 父级节点不能勾选, 则不能勾选
				if(leafTicking&&parent&&!parent.tickable){
					tickable=false;
				}
				const expandable:boolean=(isParent||lazyNotLoaded)&&_treeExpandable!==false;
				const expanded:boolean=!isParent||lazyNotLoaded?false:innerTreeExpanded.has(rawObj);
				const index=allComputedObjs.length;
				const meta:ComputedObj={
					visible,
					index,
					rawObj,
					tab,
					parent,
					isParent,
					children:[],
					key,
					lazy,
					matchesFilter:true,
					tickStrategy,
					selected:selectable&&rawObj===innerSelected,
					selectable,
					ticked:noTick
						?false
						:strictTicking
							?innerTreeTicked.has(rawObj)
							:isParent
								?false
								:innerTreeTicked.has(rawObj),
					tickable,
					expanded,
					expandable,
					noTick,
					lazyNotLoaded,
					hasTicking,
					strictTicking,
					leafTicking,
					leafFilteredTicking,
				};
				if(curFilter){
					meta.matchesFilter=curFilterMethod(getFilterMethodParam(meta));
					//末级节点, '仅勾选过滤后的末级策略' 且没有匹配, 则禁止勾选
					if(!isParent&&leafFilteredTicking&&!meta.matchesFilter){
						meta.tickable=false;
					}
				}
				metas[key]=meta;
				allComputedObjs.push(meta);
				/**
				 * ↑↑↑↑↑↑↑↑↑↑↑↑↑↑上面是, 从根节点向下的逻辑(父节点影响子节点状态)↑↑↑↑↑↑↑↑↑↑↑↑↑↑
				 * ↓↓↓↓↓↓↓↓↓↓↓↓↓↓下面是, 从末级节点向上的逻辑(子节点影响父节点状态)↓↓↓↓↓↓↓↓↓↓↓↓↓↓
				 * matchesFilter
				 * noTick
				 * tickable
				 * ticked
				 * indeterminate
				 */
				if(isParent){
					//大数据优化
					if(!expanded&&noLeafTicking&&!curFilter){
						return meta;
					}
					meta.children=[];
					for(let i=0;i<children.length;++i){
						meta.children.push(travel(children[i],meta,tab+1));
					}
					//存在过滤时, 额外操作
					if(curFilter){
						if(!meta.matchesFilter){
							/**
							 * 复核匹配状态
							 * 任何子级匹配时, 本身也该是匹配(否则, 所有子级不可见)
							 */
							meta.matchesFilter=meta.children.some((n) => n.matchesFilter);
						}
						if(
							leafFilteredTicking&&
							meta.tickable&&
							meta.children.every((n) => !n.matchesFilter||!n.tickable)
						){
							/**
							 * 对 '仅勾选过滤后的末级策略' 的额外限制
							 * 本身可以勾选, 但所有子级不能勾选, 则本身也不能勾选
							 */
							meta.tickable=false;
						}
					}
					//仅勾选末级策略
					if(leafTicking){
						//所有子级没有勾选框, 则本身也不该有勾选框
						if(meta.children.every((n) => n.noTick)){
							meta.noTick=true;
						}
						meta.ticked=false;
						//任何子级是半勾选状态, 则本身也该是半勾选状态
						meta.indeterminate=meta.children.some((n) => n.indeterminate);
						//本身可以勾选, 且至少有1个子级可以勾选, 则可以勾选
						meta.tickable=meta.tickable&&meta.children.some((n) => n.tickable);
						if(!meta.indeterminate){
							//计算勾选状态
							let all=0;
							let select=0;
							const children=meta.children;
							for(let i=0;i<children.length;++i){
								const {noTick,ticked}=children[i];
								if(!noTick){
									++all;
									if(ticked){
										++select;
									}
								}
							}
							if(all&&all===select){
								meta.ticked=true;
							}else if(select>0){
								meta.indeterminate=true;
							}
						}
						if(meta.indeterminate){
							/**
							 * 如果有可以取消的勾选, 或者任何子级可以取消勾选, 则下一次取消勾选
							 */
							meta.indeterminateNextState=!meta.children.some((n) => {
								return n.tickable&&(n.ticked||n.indeterminateNextState===false);
							});
						}
					}
				}
				return meta;
			};
			const data=this.data;
			for(let i=0;i<data.length;++i){
				travel(data[i]);
			}
			const result={
				computedObjs:allComputedObjs.filter((meta) => meta.visible&&meta.matchesFilter),
				allComputedObjs,
				metas,
			};
			return result;
		},
		//endregion
	},
	watch:{
		//region 同步 '勾选的行, 选中的行, 展开的行'
		selected(val){
			this.innerSelected=val;
		},
		treeTicked(val){
			this.innerTreeTicked=val;
		},
		treeExpanded(val){
			this.innerTreeExpanded=val;
		},
		data(val,old){
			if(val!==old){
				this.treeLazy={};
				this.$emit('reset');
			}
		},
		//endregion
	},
	methods:{
		//region 渲染树节点内容
		$_lexmis_renderNodeContent(
			h:CreateElement,
			meta:ComputedObj,
			content:VNodeChildren,
			Spinner:Component,
			Icon:Component,
			Checkbox:Component
		):VNodeChildren{
			return [
				h('div',{
					staticClass:'lexmis_tree_indent',
					staticStyle:{
						width:`${meta.tab*20}px`
					},
				}),
				meta.lazy==='loading'
					?h(Spinner,{
						staticClass:'lexmis_tree_spinner',
					})
					:(
						meta.isParent||meta.lazyNotLoaded
							?h(Icon,{
								staticClass:'lexmis_tree_arrow'+
									(meta.expanded?' lexmis--expanded':'')+
									(!meta.expandable?' lexmis--disable':''),
								props:{
									name:this.$baseui_theme!=='red'
										?'play_arrow'
										:meta.expanded
											?'remove'
											:'add'
								},
								on:{
									click:(e:Event) => {
										this.$_lexmis_onExpandClick(meta,e);
									},
								},
							})
							//箭头占位, 避免缩进样式异常
							:h('span',{
								staticClass:'lexmis_tree_arrow_placeholder'
							})
					),
				!meta.noTick
					?h(Checkbox,{
						staticClass:'lexmis_tree_checkbox',
						props:{
							value:meta.indeterminate?null:meta.ticked,
							disable:!meta.tickable
						},
						on:{
							keydown:stopAndPrevent,
							input:(v:boolean) => {
								this.$_lexmis_onTickedClick(meta,v);
							},
						},
					})
					:null,
				content,
			];
		},
		//endregion
		//region 获取过滤方法的参数
		$_lexmis_getFilterMethodParam(meta:ComputedObj):void{
			return undefined;
		},
		//endregion
		//region 点击展开
		$_lexmis_onExpandClick(meta:ComputedObj,e:Event):void{
			if(e){
				stopAndPrevent(e);
			}
			this.setExpanded(meta.key,!meta.expanded,meta);
		},
		//endregion
		//region 点击勾选
		$_lexmis_onTickedClick(meta:ComputedObj,state:boolean):void{
			if(!meta.tickable) return;
			const beforeTicked=this.beforeTicked;
			Promise.resolve(beforeTicked&&beforeTicked({meta})).then((interrupt) => {
				if(!interrupt){
					if(meta.indeterminate){
						state=meta.indeterminateNextState===true;
					}
					this.setTicked(meta.key,state,meta);
				}
			});
		},
		//endregion
		//region 计算操作后的数据
		$_lexmis_calcData(raws:MySet<RawObj>,opts:Array<Operation>):MySet<RawObj>{
			for(let i=0;i<opts.length;++i){
				const {data,state}=opts[i];
				if(state){
					raws.addAll(data);
				}else{
					raws.deleteAll(data);
				}
			}
			return raws;
		},
		//endregion
		//region 设置行的展开状态(无视限制)
		setExpandedUnlimited(opts:Array<Operation>):void{
			const target=this.$_lexmis_calcData(this.innerTreeExpanded,opts);
			if(this.treeExpanded!==undefined){
				this.$emit('update:tree-expanded',target);
			}
		},
		//endregion
		//region 设置行的勾选状态(无视限制)
		setTickedUnlimited(opts:Array<Operation>):void{
			const target=this.$_lexmis_calcData(this.innerTreeTicked,opts);
			if(this.treeTicked!==undefined){
				this.$emit('update:tree-ticked',target);
			}
			if(this.emitAllTicked){
				//当发射事件时需要下一帧才能反应改变
				this.$nextTick(function(){
					if(this.treeInfo){
						const allComputedObjs=this.treeInfo.allComputedObjs;
						const result=[];
						for(let i=0;i<allComputedObjs.length;++i){
							const {rawObj,ticked,indeterminate}=allComputedObjs[i];
							if(ticked||indeterminate){
								result.push(rawObj);
							}
						}
						// @ts-ignore
						this.$emit('all-ticked',result);
					}
				});
			}
		},
		//endregion
		//region 收集单个节点展开收缩信息
		$_lexmis_setExpanded(
			key:string,
			state:boolean,
			accordion:boolean,
			meta:ComputedObj,
			expands:(node:RawObj) => any,
			collapses:(node:RawObj) => any
		):Promise<void>{
			return new Promise((resolve) => {
				if(!meta.expandable) return resolve();
				const considerAccordion=function(context:any,meta:ComputedObj,state:boolean){
					if(state&&accordion){
						const children:Array<RawObj>=meta.parent?meta.parent.children:context.data;
						for(let i=0;i<children.length;++i){
							if(children[i].expandable){
								collapses(children[i].rawObj);
							}
						}
					}
					if(state){
						expands(meta.rawObj);
					}else{
						collapses(meta.rawObj);
					}
				};
				if(meta.lazyNotLoaded){
					if(meta.lazy==='loading'){
						return;
					}
					this.$set(this.treeLazy,key,'loading');
					const rawObj=meta.rawObj;
					if(!Array.isArray(rawObj[this.childrenKey])){
						this.$set(rawObj,this.childrenKey,[]);
					}
					this.$emit('tree-lazy-load',{
						meta,
						done:(children:Array<RawObj>) => {
							this.treeLazy[key]='loaded';
							this.$set(rawObj,this.childrenKey,Array.isArray(children)?children:[]);
							this.$nextTick(function(){
								// @ts-ignore
								const m=this.treeInfo.metas[key];
								if(m&&m.isParent){
									considerAccordion(this,m,true);
									resolve();
								}
							});
						},
						fail:() => {
							this.$delete(this.treeLazy,key);
							if(rawObj[this.childrenKey].length===0){
								this.$delete(rawObj,this.childrenKey);
							}
							resolve();
						}
					});
				}else if(meta.isParent){
					considerAccordion(this,meta,state);
					resolve();
				}
			});
		},
		//endregion
		//region 设置行的展开状态(带校验)
		setExpanded(key:string,state:boolean,meta?:ComputedObj):Promise<void>{
			if(!this.childrenKey) return Promise.reject(new Error('必须设置childrenKey!'));
			if(!meta){
				// @ts-ignore
				meta=this.treeInfo.metas[key];
			}
			const expands:Array<RawObj>=[];
			const collapses:Array<RawObj>=[];
			return this.$_lexmis_setExpanded(
				key,
				state,
				this.accordion,
				meta,
				expands.push.bind(expands),
				collapses.push.bind(collapses)
			).then(() => {
				this.setExpandedUnlimited([
					{data:collapses,state:false},
					{data:expands,state:true},
				]);
			});
		},
		//endregion
		//region 批量设置行的展开状态(带校验)
		setExpandeds(keys:Array<string>,state:boolean):Promise<void>{
			if(!this.childrenKey) return Promise.reject(new Error('必须设置childrenKey!'));
			// @ts-ignore
			const staticMetas=this.treeInfo.metas;
			const expands:Set<RawObj>=new Set<RawObj>();
			const expandsAdd=expands.add.bind(expands);
			const collapses:Set<RawObj>=new Set<RawObj>();
			const collapsesAdd=collapses.add.bind(collapses);
			return Promise.all(keys.map((key,i) => {
				return this.$_lexmis_setExpanded(
					key,
					state,
					this.accordion,
					staticMetas[key],
					expandsAdd,
					collapsesAdd,
				);
			})).then(() => {
				this.setExpandedUnlimited([
					{data:Array.from(collapses),state:false},
					{data:Array.from(expands),state:true},
				]);
			});
		},
		//endregion
		//region 展开匹配的节点(带校验)
		setMatchesExpanded(state:boolean):Promise<void>{
			if(this.treeInfo){
				const allComputedObjs=this.treeInfo.allComputedObjs;
				const result=[];
				for(let i=0;i<allComputedObjs.length;++i){
					const {key,matchesFilter,expandable}=allComputedObjs[i];
					if(matchesFilter&&expandable){
						result.push(key);
					}
				}
				return this.setExpandeds(result,state);
			}
			return Promise.reject(new Error('必须设置childrenKey!'));
		},
		//endregion
		//region 展开到当前节点(带校验)
		expandedToNode(key:string):Promise<void>{
			const meta=this.getMeta(key);
			if(meta){
				if(!meta.visible){
					const arr:Array<string>=[];
					let cur=meta.parent;
					while(cur){
						arr.push(cur.key);
						cur=cur.parent;
					}
					return this.setExpandeds(arr,true);
				}
				return Promise.resolve();
			}
			return Promise.reject(new Error('必须设置childrenKey!'));
		},
		//endregion
		//region 收集单个节点勾选信息
		$_lexmis_setTicked(state:boolean,meta:ComputedObj,add:(node:RawObj) => any):void{
			if(!meta.tickable) return;
			if(meta.strictTicking){
				add(meta.rawObj);
			}else if(meta.leafTicking){
				//仅勾选末级策略
				const travel=(meta:ComputedObj) => {
					if(meta.isParent){
						//不是末级
						if(!state&&meta.tickable){
							//准备取消勾选, 且可以勾选
							add(meta.rawObj);
						}
						if(meta.leafTicking){
							//如果当前是 '仅勾选末级策略' ,则
							const children=meta.children;
							for(let i=0;i<children.length;++i){
								travel(children[i]);
							}
						}
					}else if(
						meta.tickable&&
						(!meta.leafFilteredTicking||meta.matchesFilter)
					){
						//是末级(可以直接改变状态), 且可以勾选, 如果是 '仅勾选过滤后的末级策略' 必须是匹配的
						add(meta.rawObj);
					}
				};
				travel(meta);
			}
		},
		//endregion
		//region 设置行的勾选状态(带校验)
		setTicked(key:string,state:boolean,meta?:ComputedObj):void{
			if(!this.childrenKey) return;
			if(!meta){
				// @ts-ignore
				meta=this.treeInfo.metas[key];
			}
			const rawObjs:Array<RawObj>=[];
			this.$_lexmis_setTicked(state,meta,rawObjs.push.bind(rawObjs));
			if(rawObjs.length){
				this.$emit('tree-ticked',{
					meta,
					state,
				});
				this.setTickedUnlimited([{data:rawObjs,state}]);
			}
		},
		//endregion
		//region 树模式下, 批量设置行的勾选状态(带校验)
		setTickeds(keys:Array<string>,state:boolean,metas?:Array<ComputedObj>):void{
			if(!this.childrenKey) return;
			if(!metas){
				// @ts-ignore
				const staticMetas=this.treeInfo.metas;
				metas=keys.map((key) => {
					return staticMetas[key];
				});
			}
			const rawObjs:Set<RawObj>=new Set<RawObj>();
			const add=rawObjs.add.bind(rawObjs);
			for(let i=0;i<metas.length;++i){
				this.$_lexmis_setTicked(state,metas[i],add);
			}
			this.setTickedUnlimited([{data:Array.from(rawObjs),state}]);
		},
		//endregion
		//region 获取行的所有状态信息
		getMeta(key:string):ComputedObj|undefined{
			if(!this.childrenKey) return;
			// @ts-ignore
			return this.treeInfo.metas[key];
		},
		//endregion
		//region 获取所有行的所有状态信息
		getMetas():Array<ComputedObj>|undefined{
			if(!this.childrenKey) return;
			// @ts-ignore
			return this.treeInfo.allComputedObjs;
		},
		//endregion
	}
});
